package service

import (
	"context"
	errors2 "errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redsync/redsync/v4"
	"ylm.sl/common/biz"
	"ylm.sl/common/constant"
	"ylm.sl/common/enums"
	"ylm.sl/common/utils"
	"ylm.sl/global"
	"ylm.sl/internal/api/request"
	"ylm.sl/internal/api/response"
	"ylm.sl/internal/model"
	"ylm.sl/internal/repository/dao"
)

type IGroupService interface {
	SaveGroup(c *gin.Context, groupName string) error
	saveGroup(ctx context.Context, username string, groupName string) error
	ListGroup(c *gin.Context) (*[]response.ShortLinkGroupRespDTO, error)
	UpdateGroup(c *gin.Context, req request.ShortLinkGroupUpdateReqDTO) error
	DeleteGroup(c *gin.Context, gid string) error
	SortGroup(c *gin.Context, requestParam *[]request.ShortLinkGroupSortReqDTO) error
}

type GroupImpl struct {
	repo                                   *dao.GroupDao
	gidRegisterCachePenetrationBloomFilter *utils.BloomFilter
}

func (g *GroupImpl) SortGroup(c *gin.Context, requestParam *[]request.ShortLinkGroupSortReqDTO) error {
	// TODO 添加事务
	username := c.MustGet(enums.UserContext).(*biz.UserInfoDTO).Username
	for _, v := range *requestParam {
		group := &model.Group{
			SortOrder: v.SortOrder,
		}
		err := g.repo.UpdateByGidUsername(c, group, v.Gid, username)
		if err != nil {
			return err
		}

	}
	return nil
}

func (g *GroupImpl) DeleteGroup(c *gin.Context, gid string) error {
	username := c.MustGet(enums.UserContext).(*biz.UserInfoDTO).Username
	return g.repo.DeleteGroup(c, gid, username)
}

func (g *GroupImpl) UpdateGroup(c *gin.Context, req request.ShortLinkGroupUpdateReqDTO) error {
	group := &model.Group{
		Name: req.Name,
	}
	return g.repo.UpdateByGidUsername(c, group, req.Gid, c.MustGet(enums.UserContext).(*biz.UserInfoDTO).Username)
}

func (g *GroupImpl) ListGroup(c *gin.Context) (*[]response.ShortLinkGroupRespDTO, error) {
	username := c.MustGet(enums.UserContext).(*biz.UserInfoDTO).Username

	groupList, err := g.repo.SelectListByUserNameDesc(c, username)
	if err != nil {
		return nil, err
	}
	results := []response.ShortLinkGroupRespDTO{}

	// TODO 查询分组短链接数量
	for _, group := range *groupList {
		results = append(results, response.ShortLinkGroupRespDTO{
			Gid:            group.Gid,
			Name:           group.Name,
			SortOrder:      group.SortOrder,
			ShortLinkCount: 0,
		})
	}
	return &results, nil
}

func (g *GroupImpl) SaveGroup(c *gin.Context, groupName string) error {
	username := c.MustGet(enums.UserContext).(*biz.UserInfoDTO).Username
	return g.saveGroup(c, username, groupName)
}

func (g *GroupImpl) saveGroup(ctx context.Context, username string, groupName string) error {
	lock := global.RedSync.NewMutex(fmt.Sprintf(constant.LOCK_GROUP_CREATE_KEY, username))

	err := lock.Lock()
	if err != nil {
		return err
	}
	defer func(lock *redsync.Mutex) {
		_, err := lock.Unlock()
		if err != nil {
			global.Log.ErrContext(ctx, fmt.Sprintf("redsync lock err: %v", err))
		}
	}(lock)
	if count, err := g.repo.CountByUsername(ctx, username); err != nil || count >= int64(global.Config.ShortLink.Group.MaxNum) {
		return err
	}

	var gid string
	for retryCount, maxRetries := 0, 10; retryCount < maxRetries; retryCount++ {
		gid = g.saveGroupUniqueReturnGid(ctx)
		if gid != "" {
			group := model.Group{
				Gid:       gid,
				SortOrder: 0,
				Username:  username,
				Name:      groupName,
			}
			if err := g.repo.Insert(ctx, &group); err == nil {
				global.Log.ErrContext(ctx, "saveGroup err: %v", err)
				g.gidRegisterCachePenetrationBloomFilter.Add(ctx, gid)
				break
			}
		}
	}
	if gid == "" {
		return errors2.New("生成分组标识频繁")
	}
	return nil
}

func (g *GroupImpl) saveGroupUniqueReturnGid(ctx context.Context) string {
	gid := utils.GenerateRandom()
	if g.gidRegisterCachePenetrationBloomFilter.Contains(ctx, gid) {
		return ""
	}
	return gid
}

func NewGroupService(repo *dao.GroupDao, gidRegisterCachePenetrationBloomFilter *utils.BloomFilter) IGroupService {
	return &GroupImpl{repo: repo, gidRegisterCachePenetrationBloomFilter: gidRegisterCachePenetrationBloomFilter}
}
